//////////////////////////////////////////////////////////////////////////////////
//	 Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>
#include <wx/gbsizer.h>
#include <wx/statline.h>
#include <sstream>
#include "rcmdapp.h"
#include "mainwnd.h"
#include "stattab.h"
#include "logicmanager.h"
#include "traficgraph.h"
#include "traficcolorselector.h"
#include "levelgraph.h"

#define RTT_NAME wxT("RTT")
#define JIT_NAME wxT("Jitter")

using namespace std;

enum cIDS
{
	ID_LISTRECV=16000,
	ID_LISTTRAN,
	ID_LISTWARL,
	ID_RECV_BUTTON,
	ID_SEND_BUTTON,
	ID_INT_SELECTOR
};

enum cIntfListColumns  		 { cListColumnIntfce=0, cListColumnByte, cListColumnPacket, cListColumnErr, cListColumnDrop, cListColumnMultiCast};
enum cIntfTransmListColumns  { cListColumnColls=5, cListColumnCarrier};
enum cWirelessListColumns  	 { cWarlsColumnIntfce=0, cWarlsColumnStatus, cWarlsColumnLink, cWarlsColumnLevel, cWarlsColumnNoise, cWarlsColumnNwid, cWarlsColumnCrypt };

BEGIN_EVENT_TABLE(CStatTab, wxPanel)
	EVT_TEXT(ID_INT_SELECTOR, 	CStatTab::OnInterfaceChanged)
	EVT_ON_COLOR_CHANGED(ID_RECV_BUTTON,  CStatTab::OnRecvColor)
	EVT_ON_COLOR_CHANGED(ID_SEND_BUTTON,  CStatTab::OnSendColor)
END_EVENT_TABLE()

CStatTab::CStatTab(class wxWindow* pParent):wxPanel(pParent),
	m_tInt( 300 )
{
	m_lJIT=0;
	m_lRTT=0;


	wxGridBagSizer *sizer = new wxGridBagSizer(5, 5);

	/*
	sizer->Add(new wxStaticText(this, wxID_ANY, _("Network devices Receive:")),wxGBPosition(0,0),  wxDefaultSpan, wxUP|wxLEFT, 10);

	m_plstInfcRecv = new CLogCtrl<CStatTab>(this, ID_LISTRECV, wxSize(100,200) );
	sizer->Add(m_plstInfcRecv, wxGBPosition(1,0), wxDefaultSpan, wxUP|wxLEFT|wxEXPAND, 10);

	m_plstInfcRecv->InsertColumn( cListColumnIntfce, 	_("Interface") 	);
	m_plstInfcRecv->InsertColumn( cListColumnByte, 		_("bytes") 		);
	m_plstInfcRecv->InsertColumn( cListColumnPacket, 	_("packets") 	);
	m_plstInfcRecv->InsertColumn( cListColumnErr, 		_("errs") 		);
	m_plstInfcRecv->InsertColumn( cListColumnDrop, 		_("drop") 		);
	m_plstInfcRecv->InsertColumn( cListColumnMultiCast,	_("multicast")	);

	//Transmit
	sizer->Add(new wxStaticText(this, wxID_ANY, _("Network devices Transmit:")),wxGBPosition(0,1), wxDefaultSpan, wxUP|wxRIGHT, 10);

	m_plstInfcTrancv = new CLogCtrl<CStatTab>(this, ID_LISTTRAN);
	sizer->Add(m_plstInfcTrancv, wxGBPosition(1,1), wxDefaultSpan, wxUP|wxRIGHT|wxEXPAND, 10);

	m_plstInfcTrancv->InsertColumn( cListColumnIntfce, 		_("Interface") 	);
	m_plstInfcTrancv->InsertColumn( cListColumnByte, 		_("bytes") 		);
	m_plstInfcTrancv->InsertColumn( cListColumnPacket, 		_("packets") 	);
	m_plstInfcTrancv->InsertColumn( cListColumnErr, 		_("errs") 		);
	m_plstInfcTrancv->InsertColumn( cListColumnDrop, 		_("drop") 		);
	m_plstInfcTrancv->InsertColumn( cListColumnColls,		_("colls")		);
	m_plstInfcTrancv->InsertColumn( cListColumnCarrier,		_("carrier")	);

	sizer->AddGrowableCol(0, 0);
	sizer->AddGrowableCol(1, 0);
	*/

	int nFlags=wxLEFT|wxALIGN_CENTRE_VERTICAL;
	int nBorder=5;

	//Left selector
	wxBoxSizer* pLeftSizer = new wxBoxSizer(wxHORIZONTAL);
	pLeftSizer->Add(new wxStaticText(this, wxID_ANY, _("Interface:")), 0, nFlags, nBorder);
	m_pcIntSelector = new wxComboBox(this, ID_INT_SELECTOR, wxT(""), wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_DROPDOWN|wxCB_READONLY );
	pLeftSizer->Add(m_pcIntSelector, 0, nFlags, nBorder);
	sizer->Add(pLeftSizer, wxGBPosition(0, 0), wxDefaultSpan, wxLEFT|wxRIGHT|wxUP|wxEXPAND, 10);

	///////////////////////////////////////////////////////////////////////////////////////////////////
	//Graph control
	m_pGraph = new CTraficGraph(this);
	m_pGraph->SetTimePeriod(5*60);
	
	sizer->Add(m_pGraph, wxGBPosition(1, 0), wxGBSpan(1,3), wxALIGN_CENTER|wxLEFT|wxRIGHT|wxEXPAND, 10);
	sizer->AddGrowableRow(1, 1);
	sizer->AddGrowableCol(1, 1);

	//Traphic Labels, Receive
	wxGridBagSizer *sizerLabelsLeft = new wxGridBagSizer(10, 10);
	m_pRecvArrow = new CTraficColorSelector(this, true, ID_RECV_BUTTON);
	sizerLabelsLeft->Add(m_pRecvArrow, wxGBPosition(0, 0), wxGBSpan(2,1), wxALL|wxEXPAND|wxALIGN_CENTER, 5);
	sizerLabelsLeft->Add(new wxStaticText(this, wxID_ANY, _("Receiving:")), wxGBPosition(0,1),  wxDefaultSpan, wxUP, 5);
	sizerLabelsLeft->Add(new wxStaticText(this, wxID_ANY, _("Total Received:")), wxGBPosition(1,1),  wxDefaultSpan, wxDOWN, 5);
	m_pRecvRate = new wxStaticText(this, wxID_ANY, wxT("          "));
	sizerLabelsLeft->Add(m_pRecvRate, wxGBPosition(0,2),  wxDefaultSpan, wxUP, 5);
	m_pRecvTotal = new wxStaticText(this, wxID_ANY, wxT("          "));
	sizerLabelsLeft->Add(m_pRecvTotal, wxGBPosition(1,2),  wxDefaultSpan, wxDOWN, 5);

	//-->
	sizer->Add(sizerLabelsLeft, wxGBPosition(2,0), wxDefaultSpan, wxLEFT, 100  );

	//Traphic Labels, Send
	wxGridBagSizer *sizerLabelsRight = new wxGridBagSizer(10, 10);
	m_pTransmArrow = new CTraficColorSelector(this, false, ID_SEND_BUTTON);
	sizerLabelsRight->Add(m_pTransmArrow, wxGBPosition(0, 0), wxGBSpan(2,1), wxALL|wxEXPAND|wxALIGN_CENTER, 5);
	sizerLabelsRight->Add(new wxStaticText(this, wxID_ANY, _("Sending:")), wxGBPosition(0,1),  wxDefaultSpan, wxUP, 5);
	sizerLabelsRight->Add(new wxStaticText(this, wxID_ANY, _("Total Sent:")), wxGBPosition(1,1),  wxDefaultSpan, wxDOWN, 5);
	m_pSendRate = new wxStaticText(this, wxID_ANY, wxT("          "));
	sizerLabelsRight->Add(m_pSendRate, wxGBPosition(0,2),  wxDefaultSpan, wxUP, 5);
	m_pSentTotal = new wxStaticText(this, wxID_ANY, wxT("          "));
	sizerLabelsRight->Add(m_pSentTotal, wxGBPosition(1,2),  wxDefaultSpan, wxDOWN, 5);

	//-->
	sizer->Add(sizerLabelsRight, wxGBPosition(2,1), wxGBSpan(1,2), wxRIGHT|wxALIGN_RIGHT, 100 );
	//sizer->Add(5,5, wxGBPosition(3,0), wxGBSpan(1,3) );


	wxColor clrRecv = wxGetApp().GetMainWnd()->GetPropStorage().GetRecvColor();
	wxColor clrTran = wxGetApp().GetMainWnd()->GetPropStorage().GetTransmColor();

	m_pGraph->SetRecvColor( clrRecv );
	m_pGraph->SetTransmColor( clrTran );
	m_pRecvArrow->SetColor( clrRecv );
	m_pTransmArrow->SetColor( clrTran );



	////////////////////////////////////////////////////////////////////
	//m_pLinePing
	sizer->Add( new wxStaticLine(this, -1 ), wxGBPosition(3, 0), wxGBSpan(1,3), 0, 0);

	wxString label = _("Round Trip Time and Jitter Graph");
	sizer->Add( new wxStaticText(this, wxID_ANY, label), wxGBPosition(4, 0), wxGBSpan(1,3), wxALIGN_CENTER|wxLEFT|wxRIGHT, 0);

	m_pLinePing = new CLevelGraph(this, CLevelGraph::cNormalGraphMode, CLevelGraph::cGraphLabelBottom, wxID_ANY);

	m_pLinePing->PairUp(m_pGraph);

	m_pLinePing->SetMinSize(wxSize(100,150));

	sizer->Add(m_pLinePing, wxGBPosition(5, 0), wxGBSpan(1,3), wxLEFT|wxRIGHT|wxEXPAND, 10);

	//sizer->AddGrowableRow(5, 1);

	m_pLinePing->SetColor(RTT_NAME, *wxBLUE);
	m_pLinePing->SetColor(JIT_NAME, *wxRED);
	m_pLinePing->SetMesureName(wxT("ms"));

	ZONERANGE zone;
	ZONELIST list;

	//Bad
	zone.color = BAD_COLOR;
	zone.nFrom = 400;
	zone.nTo   = 100000;
	list.push_back(zone);

	//Fair
	zone.color = FAIR_COLOR;
	zone.nFrom = 200;
	zone.nTo   = 400;
	list.push_back(zone);

	//Good
	zone.color = GOOD_COLOR;
	zone.nFrom = 100;
	zone.nTo   = 200;
	list.push_back(zone);

	//Excelent
	zone.color = EXCELENT_COLOR;
	zone.nFrom = 0;
	zone.nTo   = 100;
	list.push_back(zone);

	m_pLinePing->SetTimePeriod(5*60);
	
	m_pLinePing->SetZones(list);

	time_t tEvent = time(NULL);
	m_pLinePing->SetData( JIT_NAME, 0,  tEvent );
	m_pLinePing->SetData( RTT_NAME, 0,  tEvent );

///////////////////////////////////////////////////////////////////////////////////////////
	SetSizer(sizer);

	m_plstInfcWireless = NULL;

	SetTimeInterval(m_tInt);
}

CStatTab::~CStatTab()
{
}

void CStatTab::SetTimeInterval(size_t tInt)
{
	m_tInt = tInt;

	m_pGraph->SetTimePeriod(m_tInt);
	m_pLinePing->SetTimePeriod(m_tInt);
}

void CStatTab::OnInterfaceChanged(wxCommandEvent& ev)
{
	int nSel = m_pcIntSelector->GetCurrentSelection();

	if ( wxNOT_FOUND!=nSel )
	{
		wxString strIntName = m_pcIntSelector->GetStringSelection();
		m_pGraph->SetInterface( strIntName );
	}
}


//IGUIBind
void CStatTab::OnResponse(const COMMANDRESPONSE& resp)
{
	switch( resp.code )
	{
		case cCmdInterfaceStat:
			OnInterfaceStat( resp.strXML );
		break;

		case cCmdPing:
		{
			OnPing( resp.strData );
		}
		break;
		
		default:
		break;
	}
}

//List
long CStatTab::GetItemCount(wxWindowID id)
{
	long lRes(0);
	
	switch (id)
	{
		case ID_LISTRECV:
		case ID_LISTTRAN:
		{
			lRes = m_tableDev.rows.size();
		}
		break;
		
		case ID_LISTWARL:
		{
			lRes = m_tableWireless.rows.size();
		}
		break;
	
	
	}
	
	return lRes;
}

wxString CStatTab::GetItemText(long item, long column, wxWindowID id)
{
	wxString str;
	
	switch(id)
	{
		case ID_LISTRECV:
		{
			str = GetRecvItemText( item,  column);
		}
		break;
		
		case ID_LISTTRAN:
		{
			str = GetTranItemText( item,  column);
		}
		break;
			
		case ID_LISTWARL:
		{
			str = GetWirelessItemText( item,  column);
		}
		break;
	}
		
	return str;
}

wxString CStatTab::GetRecvItemText(long item, long column)
{
	wxString strRes;
	
	switch(column)
	{
		case cListColumnIntfce:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "face", item, 0 );
		}	
		break;
		
		case cListColumnByte:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "bytes", item, 0 );
		}
		break;
			
		case cListColumnPacket:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "packets", item, 0 );
		}
		break;
			
		case cListColumnErr:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "errs", item, 0 );
		}
		break;
		
		case cListColumnDrop:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "drop", item, 0 );
		}
		break;
		
		case cListColumnMultiCast:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "multicast", item, 0 );
		}
		break;
	}
	
	return strRes;
}

wxString CStatTab::GetTranItemText(long item, long column)
{
	wxString strRes;
	
	switch(column)
	{
		case cListColumnIntfce:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "face", item, 0 );
		}	
		break;
		
		case cListColumnByte:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "bytes", item, 1 );
		}
		break;
			
		case cListColumnPacket:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "packets", item, 1 );
		}
		break;
			
		case cListColumnErr:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "errs", item, 1 );
		}
		break;
		
		case cListColumnDrop:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "drop", item, 1 );
		}
		break;
		
		case cListColumnColls:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "colls", item, 0 );
		}
		break;
		
		case cListColumnCarrier:
		{
			strRes = CLogicManager::GeValFromTable( m_tableDev, "carrier", item, 0 );
		}
		break;

	}
	
	return strRes;
	
}

wxString CStatTab::GetWirelessItemText(long item, long column)
{
	wxString strRes;
		
	switch(column)
	{
		case cWarlsColumnIntfce:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "face", item, 0 );
		}	
		break;
		
		case cWarlsColumnStatus:
		{
				strRes = CLogicManager::GeValFromTable( m_tableWireless, "tus", item, 0 );
		}	
		break;
		
		case cWarlsColumnLink:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "link", item, 0 );
		}
		break;
			
		case cWarlsColumnLevel:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "level", item, 0 );
		}
		break;
			
		case cWarlsColumnNoise:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "noise", item, 0 );
		}
		break;
		
		case cWarlsColumnNwid:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "nwid", item, 0 );
		}
		break;
		
		case cWarlsColumnCrypt:
		{
			strRes = CLogicManager::GeValFromTable( m_tableWireless, "crypt", item, 0 );
		}
		break;
	}
	
	return strRes;	
}



////////////////////////////////////////////////////////////////////////////////////////////
//Parse

//Inter-| sta-|   Quality        |   Discarded packets               | Missed | WE
// face | tus | link level noise |  nwid  crypt   frag  retry   misc | beacon | 22
void CStatTab::OnWirelessStat( const RTSTRING& strData )
{
	m_tableWireless = CLogicManager::ParseTableWithHeaders(strData);
}

void CStatTab::OnInterfaceStat( const RTSTRING& strData )
{
	bool bInitCombo(false);
	time_t tEvent = time(NULL);

	CMarkupSTL	doc;

	if ( doc.SetDoc(strData.c_str()) )
	{
		if ( !doc.FindElem(INTSTAT_ROOT_EL) ) return;

		if ( m_pcIntSelector->wxItemContainerImmutable::IsEmpty() )
		{
			m_pcIntSelector->Clear();
		}

		wxString strPPPName;

		while ( doc.FindChildElem(GLOBAL_ENTRY_EL) )
		{
			wxString strIntName = wxString::FromAscii( doc.GetChildAttrib(GLOBAL_NAME_ATR).c_str() );

			//Check if new interface were added
			INTLIST::iterator i = std::find(m_arrstrInterfaceList.begin(), m_arrstrInterfaceList.end(), strIntName);
			if ( i==m_arrstrInterfaceList.end() )
			{
				m_pcIntSelector->Append( strIntName );
				m_arrstrInterfaceList.push_back( strIntName );
				bInitCombo = true;
			}

			if ( wxNOT_FOUND!=strIntName.Find(wxT("ppp")) )
			{
				strPPPName = strIntName;
			}

			wxString strRecv = wxString::FromAscii( doc.GetChildAttrib(GLOBAL_RECV_ATR).c_str() );
			wxString strTransm = wxString::FromAscii( doc.GetChildAttrib(GLOBAL_TRNSM_ATR).c_str() );

			unsigned long long lVal=0;
			strRecv.ToULongLong(&lVal);
			m_pGraph->SetRecv( strIntName, lVal, tEvent );

			strTransm.ToULongLong(&lVal);
			m_pGraph->SetTransm( strIntName, lVal, tEvent );
		}

		//Auto select PPP interface if any
		if ( bInitCombo )
		{
			//may be ppp0 or ppp_0_8_48_1 or what ever ppp...
			int nPos = m_pcIntSelector->FindString(strPPPName);
			int nSel = m_pcIntSelector->GetCurrentSelection();

			if ( nSel==wxNOT_FOUND )
			{
				if (wxNOT_FOUND!=nPos)
					m_pcIntSelector->Select(nPos);
				else
					m_pcIntSelector->Select(0);


				wxCommandEvent ev(1);
				OnInterfaceChanged( ev );
			}
		}
	}

	m_pRecvRate->SetLabel( m_pGraph->GetRecvRate() );
	m_pRecvTotal->SetLabel( m_pGraph->GetRecvTotal() );

	m_pSendRate->SetLabel( m_pGraph->GetSendRate() );
	m_pSentTotal->SetLabel( m_pGraph->GetSendTotal() );
}

void CStatTab::OnPing( const RTSTRING& resp )
{
	time_t tEvent = time(NULL);

	stringstream sIn( (resp+"\n").c_str() );

	wxString strSite;
	char buf[512];
	char tmp[512];
	int nFnd;
	long lTransm, lRecv;
	enum cStages { cStageHeader=1, cStagePCount=1<<1, cStageRTT=1<<2, cStageDone =  cStageHeader|cStagePCount|cStageRTT};
	long lStatus = 0;

	m_lJIT = 0;
	m_lRTT = 0;

	do 
	{
		sIn.getline(buf, sizeof(buf));

		nFnd = sscanf(buf, "PING %s", tmp);

		if ( 1==nFnd )
		{
			strSite = wxString::FromAscii(tmp);

			if (!strSite.empty())
			{
				lStatus|=cStageHeader;
				continue;
			}
		}

		nFnd = sscanf(buf, "%d packets transmitted, %d packets received", &lTransm, &lRecv );

		if (2==nFnd)
		{
			if ( lTransm  == lRecv )
			{
				lStatus|=cStagePCount;
			}
			else
			{

			}
			continue;
		}

		char* pRound = strstr(buf, "round-trip min/avg/max" );
		float f[3]={0,0,0};

		if ( pRound )
		{
			bool bSeekDigit = true;
			int nCunt=0;

			do
			{
				if ( bSeekDigit )
				{
					if ( isdigit(*pRound) )
					{
						f[nCunt++] = atof(pRound);
						if (nCunt==3)
						{
							lStatus|=cStageRTT;
							break;
						}
						bSeekDigit = false;
					}
				}
				else if (*pRound=='/')
				{
					bSeekDigit = true;
				}
			}while(*++pRound!=0);
		}

		if ( (lStatus&cStageDone) == cStageDone )
		{
			unsigned long lJit=abs(f[2]-f[0]);

			if( f[0]>60*60 ||
				f[1]>60*60 ||
				f[2]>60*60 )
			{
				lJit = 0;
				f[1] = 0;
			}

			m_lJIT = lJit;
			m_lRTT = f[1];

			m_pLinePing->SetData( JIT_NAME, lJit, tEvent );
			m_pLinePing->SetData( RTT_NAME, f[1], tEvent );

			lStatus = 0;
		}

		char* pUnk = strstr(buf, "Unknown host" );

		if ( pUnk )
		{
			m_pLinePing->SetData( JIT_NAME, 0, tEvent );
			m_pLinePing->SetData( RTT_NAME, 0, tEvent );

			break;
		}

	}while( !sIn.eof() );
}

void CStatTab::Reset()
{
	m_lJIT=0;
	m_lRTT=0;

	m_pGraph->Reset();
	m_pLinePing->Reset();

	
	m_pRecvRate->SetLabel( m_pGraph->GetRecvRate() );
	m_pRecvTotal->SetLabel( m_pGraph->GetRecvTotal() );

	m_pSendRate->SetLabel( m_pGraph->GetSendRate() );
	m_pSentTotal->SetLabel( m_pGraph->GetSendTotal() );
}

void CStatTab::OnRecvColor(wxCommandEvent& ev)
{
	wxColor clr =  m_pRecvArrow->GetColor();
	m_pGraph->SetRecvColor( clr );
	wxGetApp().GetMainWnd()->GetPropStorage().SetRecvColor( clr );
}

void  CStatTab::OnSendColor(wxCommandEvent& ev)
{
	wxColor clr =  m_pTransmArrow->GetColor();
	m_pGraph->SetTransmColor( clr );
	wxGetApp().GetMainWnd()->GetPropStorage().SetTransmColor( clr );
}

wxString CStatTab::GetLog()
{
	wxString strStatus;

	if ( wxGetApp().GetMainWnd()->GetPropStorage().GetEnableBandwidthLog() )
	{
		strStatus = wxString::Format( wxT("%s %s, %s %s, %s %s, %s %s, RTT: %u ms, Jitter: %u ms"),
			_("Receiving:"), m_pGraph->GetRecvRate().c_str(),
			_("Total Received:"), m_pGraph->GetRecvTotal().c_str(),
			_("Sending:"), m_pGraph->GetSendRate().c_str(),
			_("Total Sent:"), m_pGraph->GetSendTotal().c_str(),
			m_lRTT, m_lJIT);
	}

	return strStatus;
}
